package uk.ac.cam.cl.oscar.sift;

import java.util.Vector;
import java.awt.image.BufferedImage;

import uk.ac.cam.cl.oscar.*;

/**
 * The workhorse class - calculates the keypoints for a given
 * image and spits them out.
 */
public class SIFT
{
    public static final double sigma = 1.6;
    // Minimum size for the scaled images in the pyramid
    public static final int minSize = 32;
    // Number of levels in each octave of the pyramid
    public static final int levels = 3;
    // The ratio that defines an edge
    public static final double edgeRatio = 10.0;
    public static final double peakthreshold = 0.3; /* 1.0 being complete change, 0.0 none. A low value is advisable */

    public static Vector<Keypoint> findKeypoints(BufferedImage image)
    {
        Vector<Keypoint> result = new Vector<Keypoint>();
        
        double scalefactor = 1;
        
        BufferedImage imageScaled = ImageOperations.scale(image, scalefactor);

        // Build our pyramid
        OctavePyramid pyramid = new OctavePyramid();
        pyramid.makeOctaves(imageScaled, levels, sigma, minSize);

        for (int n=0; n<pyramid.getLevels(); n++) {
            Octave oct = pyramid.getLevel(n);

            // Find the peaks for each octave
            Vector<Peak> peaks = oct.findPeaks(peakthreshold);

            // Filter them to get the best
            peaks = oct.filterPeaks(peaks, edgeRatio);

            // Make the gradient maps
            oct.makeDescriptors();

            // And use all of the above to find the keypoints
            Vector<Keypoint> temp = oct.generateKeypoints(peaks, 1.0, sigma, levels);
            result.addAll(temp);
            
            //System.out.println ("Analysed pyramid level " + n + " / " + pyramid.getLevels());
        }
        
        /* Since we doubled the size of the image on the way in, we shrink the coordinates before passing them back
        	I'm leaving all params as are, and just halfing x and y. This is probably slightly incongrous with the
        	theory, but I am not sure of the parameter's significance enough to make a better decision
        	*/
        
        for (int i = 0; i < result.size(); i++) {
        	Keypoint p = result.elementAt(i);
        	p.setX (p.getX()*(1/scalefactor));
        	p.setY (p.getY()*(1/scalefactor));
        	
        }
        
        return result;
    }
}
